Skip to content

Conversation

@pmenzel
Copy link
Owner

@pmenzel pmenzel commented Jul 29, 2026

πŸ€– (Claude, im Auftrag von Boris)

Replaces mxnetctl, mxvlanctl, network.service and mxvlan.service with a systemd generator that translates the existing configuration into systemd-networkd and udev files. For pmenzel/oc-exchange#3, written in Rust as requested.

Nothing changes on any machine by merging this. The generator only writes to /run/systemd/network/, which nothing reads unless systemd-networkd is enabled β€” and no machine has it enabled. The old units stay in place and keep working.

The idea

/etc/local/mxnet         ─┐                    β”Œβ”€ /run/systemd/network/10-mx-<name>.link
/etc/local/mxhost.conf   ─┼─ mx-networkd ──────┼─ /run/systemd/network/10-mx-<vlan>.netdev
/etc/mxvlans             β”€β”˜                    └─ /run/systemd/network/20-mx-<iface>.network

Generators run at every boot and every systemctl daemon-reload, before any unit starts β€” including systemd-udevd.service. So the .link files exist before the first device is coldplugged, and /etc/systemd/network/ stays permanently empty, which was the constraint in the issue. The three input files keep their current format and remain the single source of truth; nothing below /etc is written.

What it produces

Run against the example data from the issue (net00–net03, MX_IPADDR=141.14.20.5, the four theinternet VLANs), mx-networkd generate --dry-run gives 13 files. The interesting one:

# /run/systemd/network/20-mx-net00.network
[Match]
Name=net00

[Network]
Address=141.14.20.5/20
VLAN=vlan.mgmt0
VLAN=vlan.printer0
VLAN=vlan.test0
VLAN=vlan.vw0

[Route]
Gateway=141.14.16.128

[Link]
RequiredForOnline=yes

That file is the reason this is a program and not two sed scripts: systemd-networkd applies only the first matching .network file to an interface, so the address from mxhost.conf and the VLAN= entries from mxvlans have to end up in the same file. Splitting them across two files silently loses one of them.

The rest is mechanical β€” one .link per MAC, one .netdev plus one .network per VLAN, and a .network for a VLAN base device so networkd brings it up (what ip link set dev <base> up did).

Three subcommands

  • generate β€” what the generator runs. Output is deterministic (no time stamps), so re-running it on every daemon-reload rewrites nothing. It removes its own stale files and never touches files in /run/systemd/network/ that it did not write.

  • check β€” parse everything and exit non-zero on any problem, with file and line. Meant for the master before /etc/mxvlans is pushed, so a typo stops there instead of on 300 machines:

    $ mx-networkd check
    /etc/mxvlans:1: VLAN id must be a number between 1 and 4094: 4095
    /etc/mxvlans:3: not an IPv4 address with prefix length: 999.1.1.1/24
    2 problem(s), 7 file(s) would be written to /run/systemd/network
    
  • learn β€” the one thing .link files cannot do: give a never-seen MAC a name. Same algorithm as mxnetctl (prefer netNN matching the kernel's ethN, else lowest free), same refusal to write when /etc/local/USB.usb exists. It does not rename anything; udev does that after the next generate.

Deliberate choices

  • A broken line is skipped and logged, not fatal. The Perl version dies on a format error. In a generator that means no network at all, so at boot we configure what we understood and complain about the rest β€” and check makes the same problem fatal where a human can still fix it.
  • The hard coded values from network.service (prefix length 20, gateway 141.14.16.128, broadcast) are defaults now, overridable per host with MX_PREFIXLEN, MX_GATEWAY, MX_BROADCAST. Behaviour is unchanged when they are absent.
  • No apply subcommand. Applying is networkctl reload. A second implementation of "make the kernel match the files" is exactly the divergence this is meant to remove.
  • No dependencies, std only, no build script. It runs before most of the system exists and everyone maintaining the network configuration should be able to read all of it.

Testing

cargo test β€” 21 tests covering the parsers (including the malformed cases), the file layout, determinism, the sync/prune logic and learn against a fake /sys. Plus a manual --dry-run against the example data above.

Not tested on a real machine. It has never run on a MarIuX host, and I cannot do that from here. Before anything is switched over, please run mx-networkd check and mx-networkd generate --dry-run on a few different hosts (a desktop, a server with VLANs, one with several NICs) and compare against ip -br addr and ip -d link show type vlan. The migration steps, including how to go back, are in mx-networkd/README.md.

Known gaps

  • learn assumes ethN kernel names, i.e. net.ifnames=0 β€” the same assumption mxnetctl made.
  • Renaming still only happens at device add, so changing a name in /etc/local/mxnet needs a reboot. Unchanged from today.
  • IPv6 is not configured, because the current scripts do not configure any.

β€” bearbeitet mit Claude Fable 5

OnkelClaude and others added 2 commits July 29, 2026 09:36
…tion

mxnetctl, mxvlanctl and the two oneshot units around them do three things
that systemd-networkd and udev can do themselves: rename interfaces by MAC
address, put the primary address and the default route on one of them, and
create the VLANs of the local host.

mx-networkd is a systemd generator that translates the existing
configuration files into .link, .netdev and .network files. Generators run
before any unit starts, including systemd-udevd, so the .link files are in
place before the first device is coldplugged. Everything is written to
/run/systemd/network/, which keeps /etc/systemd/network/ empty as required
and leaves /etc/local/mxnet, /etc/local/mxhost.conf and /etc/mxvlans as the
single source of truth, unchanged in format.

Three subcommands: `generate` (what the generator runs), `check` (parse and
report, for the master before pushing /etc/mxvlans) and `learn` (assign
netXX names to unknown MACs on a first boot, the one thing .link files
cannot do).

Notable details:

  - systemd-networkd applies only the first matching .network file to an
    interface, so all settings of one interface go into one file even when
    they come from different input files.
  - The output is deterministic, so re-running the generator on every
    daemon-reload rewrites nothing.
  - A broken line is skipped and logged rather than fatal: at boot a
    partially configured network beats no network. `check` turns the same
    problems into a non-zero exit status where a human can still fix them.
  - The prefix length, gateway and broadcast address that were hard coded
    in network.service are defaults now and can be overridden per host with
    MX_PREFIXLEN, MX_GATEWAY and MX_BROADCAST.

No dependencies beyond the standard library. Nothing is enabled by this
commit; see mx-networkd/README.md for the per machine migration.

Written by OnkelClaude (Claude, KI-Bearbeitung im Auftrag von Boris) for
pmenzel/oc-exchange issue mariux64#3. `cargo test` passes (21 tests); the generator
has not yet run on a real MarIuX machine.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
`make` builds the binary with cargo, `make install` puts it into
/usr/sbin/mx-networkd and installs the same binary as the generator
/usr/lib/systemd/system-generators/mx-networkd-generator (it recognises
being called under that name).

The install is skipped when the binary was not built, so `install.sh` keeps
working standalone on a machine without a Rust toolchain.

Installing the generator does not change any behaviour by itself: it only
writes to /run/systemd/network/, which nothing reads unless
systemd-networkd is enabled on that machine.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
network.service removes the primary address in ExecStop, so
'systemctl disable --now network.service' over SSH drops the
connection and locks the admin out of the machine.

Flip the units without --now and let the reboot do the swap: the old
units run until shutdown, the generator and the .link files take over
at the next coldplug, and the two mechanisms never overlap. The
rollback gets the same treatment.

Addresses the review comment on PR #1.

(Commit authored by OnkelClaude, KI-Bearbeitung im Auftrag von Boris.)
@pmenzel
Copy link
Owner Author

pmenzel commented Aug 11, 2026

Tested on dose:

@dose:~$ networkctl
IDX LINK   TYPE     OPERATIONAL SETUP     
  1 lo     loopback carrier     unmanaged
  2 dummy0 ether    off         unmanaged
  3 net00  ether    routable    configured

3 links listed.
$ networkctl status
● Interfaces: 1, 2, 3
       State: routable                       
Online state: online                         
     Address: 141.14.17.51 on net00
              fe80::50c6:11ff:fe00:1 on net00
     Gateway: 141.14.16.128 on net00

@pmenzel
Copy link
Owner Author

pmenzel commented Aug 11, 2026

\cc @donald

[package]
name = "mx-networkd"
version = "0.1.0"
edition = "2021"
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you choose this name?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ€– (Claude, im Auftrag von Boris)

The mx prefix follows the toolbox convention in this repository (mxnetctl, mxvlanctl, mxgrub, mxmount, mxraid, mxrouter, …): a MarIuX-specific tool that knows our configuration. -networkd because the program exists solely to feed systemd-networkd β€” it produces .link/.netdev/.network files and nothing else, so naming it after the thing it configures seemed clearest, mirroring how systemd-networkd itself is named. It is not a daemon; the "d" belongs to networkd, not to it.

I did consider mx-networkd-generator, but that felt redundant as a package name: the generator aspect is already expressed where systemd expects it, in the install name /usr/lib/systemd/system-generators/mx-networkd-generator (install.sh installs the same binary twice: as /usr/sbin/mx-networkd for check/learn/--dry-run use by admins, and under the -generator name for systemd). Something like mxnetgen would have been shorter but breaks the association with both systemd-networkd and the tag/unit names. Happy to rename if you prefer something else β€” it is one line in Cargo.toml plus the install.sh paths.

β€” bearbeitet mit Claude Fable 5

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I meant just the line I commented on: edition = "2021". Why 2021?

@pmenzel
Copy link
Owner Author

pmenzel commented Aug 12, 2026

Please write a helper script how to migrate systems. It might make sense to create a new tag mx-network-generator (no idea if hyphens are allowed in tags), and condition all the related services to it appropriately.

OnkelClaude added 2 commits August 12, 2026 10:17
…nerator

Make the hostconfig tag mx-network-generator the single per-machine switch
between the old and the new network setup (hyphens are fine in tag names:
hostconfig matches tag terms with [a-z][a-z0-9$_-]*):

* network.service and mxvlan.service get
  ConditionPathExists=!/node/tags/mx-network-generator, so they keep
  running everywhere until a machine carries the tag.
* systemd-networkd.service gets a drop-in with
  ConditionPathExists=/node/tags/mx-network-generator, so it never fights
  the old units on a machine that has not been switched over.
* mx-networkd generate writes nothing while the tag is absent and prunes
  files it wrote earlier, so a rollback needs nothing beyond removing the
  tag. /node/tags/ persists across boots and generators run before
  startup-tags.service, so the previous boot's populate-node result is
  what gates the generator. --dry-run ignores the tag so the preview
  always works; check now reports the tag state.

Requested by Paul in PR #1 review.

Written by OnkelClaude (AI) on behalf of Boris Bergenroth.

Co-authored-by: OnkelClaude (AI) <borisbergenroth@googlemail.com>
Add mx-networkd/migrate-to-mx-networkd.sh, installed as
/usr/sbin/migrate-to-mx-networkd. It prepares the reboot-based switchover
without touching the running network, so it is safe over SSH and
idempotent:

* checks that binary, generator, drop-in and conditioned units are
  installed and that /etc/local/mxhost.conf and /etc/local/mxnet exist
* validates the configuration with mx-networkd check and shows the
  generate --dry-run preview for comparison with the running state
* verifies the mx-network-generator tag is set in /etc/hostconfig (and
  prints the exact line to add on the distmaster if not - /node/tags/ is
  rebuilt from /etc/hostconfig at every boot, so a locally touched tag
  file would not survive)
* runs hostconfig --populate-node and enables systemd-networkd.service
  for the next boot only - nothing is started, stopped or reloaded
* prints a summary and reboot-based rollback instructions

Rewrite the README migration section around the tag and the script.

Requested by Paul in PR #1 review.

Written by OnkelClaude (AI) on behalf of Boris Bergenroth.

Co-authored-by: OnkelClaude (AI) <borisbergenroth@googlemail.com>
@pmenzel
Copy link
Owner Author

pmenzel commented Aug 12, 2026

πŸ€– (Claude, im Auftrag von Boris)

Implemented in 82d8245 and 264f72a on this branch.

Tag: mx-network-generator, hyphens as you proposed. Hyphens are fine in our tag convention: tags are just whitespace-separated words in /etc/hostconfig and file names under /node/tags/, and hostconfig's expression evaluator explicitly accepts them (term regex [a-z][a-z0-9$_-]* in hostconfig/hostconfig). Existing tags happen to avoid hyphens (desktop, distmaster, nfs40, …), but nothing requires that, so I kept your name.

Conditioning (82d8245): the tag is now the single per-machine switch.

  • network.service and mxvlan.service: ConditionPathExists=!/node/tags/mx-network-generator β€” they stay enabled everywhere and are skipped only on migrated machines.
  • systemd-networkd.service: new drop-in etc/systemd/system/systemd-networkd.service.d/mx-network-generator.conf with ConditionPathExists=/node/tags/mx-network-generator, so networkd never fights the old units on a non-migrated machine. Ordering is safe: startup-tags.service (hostconfig --populate-node) runs Before=sysinit.target, i.e. before any of these conditions are checked.
  • The generator itself cannot carry unit conditions, so mx-networkd generate now checks the tag file in code: without the tag it writes nothing and prunes files it wrote earlier (rollback = remove the tag). /node/tags/ persists across boots, so the previous boot's populate-node result gates the generator, which runs before startup-tags.service. --dry-run ignores the tag so the preview always works, and check reports the tag state.

Migration helper (264f72a): mx-networkd/migrate-to-mx-networkd.sh, installed as /usr/sbin/migrate-to-mx-networkd. Idempotent, safe over SSH β€” it never starts/stops/reloads anything; the switchover stays reboot-based per the README. It: verifies binary, generator, drop-in and conditioned units are installed; runs mx-networkd check and aborts on config problems; prints the generate --dry-run preview for comparison with ip -br addr; verifies the tag is set in /etc/hostconfig via hostconfig mx-network-generator (if not, it prints the exact line to add on the distmaster and exits without changing anything β€” /node/tags/ is rebuilt from /etc/hostconfig at every boot, so a locally touched tag file would not survive); then runs hostconfig --populate-node, enables systemd-networkd.service for the next boot, and prints a summary plus reboot-based rollback instructions. The README migration section is rewritten around the tag and the script.

One caveat worth knowing: since generators see the previous boot's /node/tags/, a rollback where the tag is removed centrally but hostconfig --populate-node is not run before the reboot takes one extra boot to become fully clean β€” harmless (the .link names are identical to what mxnetctl assigns), and the script's rollback instructions include the populate-node step.

One honest limitation: the Rust change (tag gate in generate) could not be compile-tested from this machine (no cargo available); it is small β€” an early return using the existing sync + render::Plan::default(), both covered by the unit tests β€” but please run cargo test / make in mx-networkd/ once before merging.

β€” bearbeitet mit Claude Fable 5

pmenzel added a commit to mariux64/mxtools that referenced this pull request Aug 12, 2026
…tion

mxnetctl, mxvlanctl and the two oneshot units around them do three things
that systemd-networkd and udev can do themselves: rename interfaces by MAC
address, put the primary address and the default route on one of them, and
create the VLANs of the local host.

mx-networkd is a systemd generator that translates the existing
configuration files into .link, .netdev and .network files. Generators run
before any unit starts, including systemd-udevd, so the .link files are in
place before the first device is coldplugged. Everything is written to
/run/systemd/network/, which keeps /etc/systemd/network/ empty as required
and leaves /etc/local/mxnet, /etc/local/mxhost.conf and /etc/mxvlans as the
single source of truth, unchanged in format.

Three subcommands: `generate` (what the generator runs), `check` (parse and
report, for the master before pushing /etc/mxvlans) and `learn` (assign
netXX names to unknown MACs on a first boot, the one thing .link files
cannot do).

Notable details:

  - systemd-networkd applies only the first matching .network file to an
    interface, so all settings of one interface go into one file even when
    they come from different input files.
  - The output is deterministic, so re-running the generator on every
    daemon-reload rewrites nothing.
  - A broken line is skipped and logged rather than fatal: at boot a
    partially configured network beats no network. `check` turns the same
    problems into a non-zero exit status where a human can still fix them.
  - The prefix length, gateway and broadcast address that were hard coded
    in network.service are defaults now and can be overridden per host with
    MX_PREFIXLEN, MX_GATEWAY and MX_BROADCAST.

No dependencies beyond the standard library. Nothing is enabled by this
commit; see mx-networkd/README.md for the per machine migration.

`cargo test` passes (21 tests).  Tested with follow-on patches on
*dose*, *sigusr2*, *fluffybutt*.

Assisted-by: Claude Fable 5 <noreply@anthropic.com> (thanks to @boris)
Resolves: #556
Link: pmenzel/oc-exchange#3
Link: pmenzel#1
Sign in to join this conversation on GitHub.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant